import type {Context, ReactNode,} from 'react'
import React, {createContext, useContext, useRef} from "react";
import {useOnce} from "./hook";
import type {TopDesignContext} from "./context.type";
import {useHooksOnDesign} from "./lifecycle";

/**
 * inject函数重载类型
 * @author  韦胜健
 * @date    2021/3/9 18:05
 */
export interface Injection {
    <T = any>(key: string): T;

    <T = any>(key: string, defaultValue: T): T;
}

/**
 * inject、provide以及一些生命周期钩子函数都是通过调用TopDesign对象中的方法实现调用所在上下文的内容的
 * @author  韦胜健
 * @date    2021/3/9 18:06
 */
export const TopDesign = {ctx: null as null | TopDesignContext,}

/**
 * 通过name得到一个 React.createContext 对象
 * @author  韦胜健
 * @date    2021/2/25 22:26
 */
export const getContextByName = (() => {
    const map = {} as { [k: string]: Context<any> }
    return (name: string) => {
        if (!map[name]) {
            map[name] = createContext(undefined)
        }
        return map[name]
    }
})();

/**
 * 向子节点提供数据
 * @author  韦胜健
 * @date    2021/3/9 18:06
 */
export function provide(name: string, value: any) {
    if (!TopDesign.ctx) {throw new Error('只能在setup函数执行的时候执行provide函数')}
    return TopDesign.ctx.useProvide(name, value)
}

/**
 * 子节点从父节点注入数据
 * @author  韦胜健
 * @date    2021/3/9 18:06
 */
export const inject: Injection = (key: string, defaultValue?: any) => {
    if (!TopDesign.ctx) {throw new Error('只能在setup函数执行的时候执行inject函数')}
    const hookData = useHooksOnDesign(() => {
        const injectValue = useRef()
        const ctxValue = useContext(getContextByName(key))
        if (ctxValue !== undefined) {
            /*inject得到的值不为undefined，表示有值*/
            injectValue.current = ctxValue
        } else {
            if (defaultValue === undefined) {
                /*defaultValue为undefined表示没有默认值，表示必须inject到值。此时ctxValue为undefined表示inject不到值，这里抛出异常*/
                console.warn(`injection "${key}" not found.`)
                return undefined
            } else {
                injectValue.current = defaultValue
            }
        }
        return injectValue
    })
    return hookData.current!.current
}

/**
 * 获取当前ctx
 * @author  韦胜健
 * @date    2021/3/15 11:01
 */
export const getCurrentDesignInstance = () => {
    if (!TopDesign.ctx) {throw new Error('只能在setup函数执行的时候执行getCurrentDesignInstance函数')}
    return TopDesign.ctx
}

export function useProvideManager() {
    return useOnce(() => {
        const providers = [] as { name: string, data: any }[]
        return {
            provideManager: {
                useProvide: (name: string, data: any) => {
                    providers.push({name, data})
                },
                wrap: (renderContent: ReactNode) => providers.reduce((prev, item) => {
                    const CTX = getContextByName(item.name)
                    return (<CTX.Provider value={item.data}>{prev}</CTX.Provider>)
                }, renderContent) as any
            },
        }
    })
}
